
#include "galaxy_render.h"

GalaxyRender::GalaxyRender()
{
  this->pickingMode = GAL_RENDER_PICKING_MODE_OFF;
  this->sysToPrint = GAL_RENDER_NO_SYS_TO_DISPLAY;
  this->vortexToPrint = GAL_RENDER_NO_SYS_TO_DISPLAY;
  this->setMouseTracking(true);
  
  this->printLinks = true;
}

GalaxyRender::~GalaxyRender()
{
  
}

void		GalaxyRender::setParent(QWidget *parent)
{
  this->parent = parent;
}

void		GalaxyRender::init(GameCore *gameCore)
{
  this->gameCore = gameCore;
  this->xRot = 0.0f;
  this->yRot = 0.0f;
  this->zoom = -0.2f;
}

QSize		GalaxyRender::sizeHint() const
{
  return QSize(600, 600);
}

QSize		GalaxyRender::minimumSizeHint() const
{
  return QSize(400, 400);
}

void		GalaxyRender::rotateLeft()
{
  this->xRot += -5.0f;
  this->updateGL();
}

void		GalaxyRender::rotateRight()
{
  this->xRot += 5.0f;
  this->updateGL();
}

void		GalaxyRender::rotateUp()
{
  this->yRot += -5.0f;
  this->updateGL();
}

void		GalaxyRender::rotateDown()
{
  this->yRot += 5.0f;
  this->updateGL();
}

void		GalaxyRender::zoomIn()
{
  this->zoom += 0.1f;
  if(this->zoom < -0.4f)
    this->zoom = -0.4f;
  if(this->zoom > 0.8f)
    this->zoom = 0.8f;
  this->updateGL();
}

void		GalaxyRender::zoomOut()
{
  this->zoom += -0.1f;
  if(this->zoom < -0.4f)
    this->zoom = -0.4f;
  if(this->zoom > 0.8f)
    this->zoom = 0.8f;
  this->updateGL();
}

void		GalaxyRender::resetView()
{
  this->xRot = 0.0f;
  this->yRot = 0.0f;
  this->zoom = -0.2f;
  this->updateGL();
}

void		GalaxyRender::showLinks(int state)
{
  switch(state)
    {
    case Qt::Unchecked:
      this->printLinks = false;
      break;
    case Qt::Checked:
      this->printLinks = true;
      break;
    }
  this->updateGL();
}

void		GalaxyRender::mouseMoveEvent(QMouseEvent *event)
{
  this->pickingMode = GAL_RENDER_PICKING_MODE_ON;
  this->xToPick = event->x();
  this->yToPick =  event->y();
  
  // needed to resize otherwise Qt disturb the ratio...
  this->resizeGL(this->windowW, this->windowH);
  this->paintGL();
  this->updateGL();
  this->sysToPrint = GAL_RENDER_NO_SYS_TO_DISPLAY;
  this->vortexToPrint = GAL_RENDER_NO_SYS_TO_DISPLAY;
}

void		GalaxyRender::initializeGL()
{
  glEnable(GL_TEXTURE_2D);
  // anti aliasing
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEnable(GL_BLEND);
  // for points
  glEnable(GL_POINT_SMOOTH);
  glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
  // for lines
  glEnable(GL_LINE_SMOOTH);
  glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
  // for polygons
  glEnable(GL_POLYGON_SMOOTH);
  glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

  // Black background
  //glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
  qglClearColor(Qt::black);
  glShadeModel(GL_FLAT);
}

void		GalaxyRender::resizeGL(int w, int h)
{
  GLfloat	fAspect;
  
  // Prevent a divide by zero
  if(h == 0)
    h = 1;
  
  this->windowH = h;
  this->windowW = w;

  // Set Viewport to window dimensions
  glViewport(0, 0, w, h);

  fAspect = (GLfloat)w/(GLfloat)h;
  this->ratio = fAspect;
  // Reset coordinate system
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  gluPerspective(45.0f, fAspect, 1.0, 1000.0);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void		GalaxyRender::paintGL()
{
  int		i, size, objHit;
  std::vector<SolarSystem*> *systems;
  SolarSystem	*star;
  QFont serifFont("Times", 10, QFont::Bold);
  
  // Clear the window with current clearing color
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  if(this->pickingMode == GAL_RENDER_PICKING_MODE_ON)
    this->startPicking(this->xToPick, this->yToPick);

  // Save matrix state and do the rotation
  glPushMatrix();

  glTranslatef(0.0f, 0.0f, -600.0f);
  glRotatef(this->yRot, 1.0f, 0.0f, 0.0f);
  glRotatef(this->xRot, 0.0f, 1.0f, 0.0f);
  // zoom
  glScalef(1.0f + this->zoom, 1.0f + this->zoom, 1.0f + this->zoom);
  glShadeModel(GL_SMOOTH);
  
  glInitNames();
  glPushName(0);
  systems = this->gameCore->getSolarSystems();
  for(i = 0, size = systems->size(); i < size; i++)
    {
      star = systems->at(i);
      // color switch
      switch(star->getColor())
	{
	case SOL_SYS_COLOR_RED:
	  glColor3f(1.0f, 0.0f, 0.0f);
	  break;
	case SOL_SYS_COLOR_YELLOW:
	  glColor3f(0.937f, 0.898f, 0.546f);
	  break;
	case SOL_SYS_COLOR_WHITE:
	  glColor3f(1.0f, 1.0f, 1.0f);
	  break;
	case  SOL_SYS_COLOR_BLUE:
	  glColor3f(0.117f, 0.562f, 1.0f);
	  break;
	}
      switch(star->getSize())
	{
	case SOL_SYS_BIG:
	  glPointSize(GAL_RENDER_SOL_BIG_SIZE);
	  break;
	case SOL_SYS_MEDIUM:
	  glPointSize(GAL_RENDER_SOL_MID_SIZE);
	  break;
	case SOL_SYS_SMALL:
	  glPointSize(GAL_RENDER_SOL_SMA_SIZE);
	  break;
	}
  
      glLoadName(i + 1);
      glBegin(GL_POINTS);
      glVertex3f(star->getX(), star->getY(), star->getZ());
      glEnd();
    }
  glColor3f(1.0f, 0.0f, 1.0f);
  
  if(this->printLinks == true)
    this->drawVortex();
  glPopMatrix();
	  
  if(pickingMode == GAL_RENDER_PICKING_MODE_ON)
    {
      if((objHit = this->stopPicking(1)) > 0 && objHit < 1000)
	this->sysToPrint = objHit - 1;
      if(objHit >= 1000 && this->printLinks == true)
	this->vortexToPrint = objHit -1000;
    }
  else
    {
      if(sysToPrint != GAL_RENDER_NO_SYS_TO_DISPLAY)
	  this->showSystemInfo(this->sysToPrint);
      glFlush();
    }
}

void		GalaxyRender::drawVortex()
{
  int		i;
  SolarSystem	*star1;
  SolarSystem	*star2;

  glPointSize(1);
  for(i = 0; i < (int)this->gameCore->vortexList.size(); i++)
    {
      star1 = this->gameCore->systems.at(this->gameCore->vortexList.at(i)->sysId1);
      star2 = this->gameCore->systems.at(this->gameCore->vortexList.at(i)->sysId2);
      switch(this->gameCore->vortexList.at(i)->linkType)
	{
	case 0:
	  glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
	  if(this->vortexToPrint == i)
	    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	  break;
	case 1:
	  glColor4f(0.5f, 0.0f, 0.0f, 0.5f);
	  if(this->vortexToPrint == i)
	    glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
	  break;
	}
      glLoadName(i + 1000);
      glBegin(GL_LINES);
      glVertex3f(star1->getX(), star1->getY(), star1->getZ());
      glVertex3f(star2->getX(), star2->getY(), star2->getZ());
      glEnd();
    }
}

void		GalaxyRender::showSystemInfo(int system)
{
  std::string	str;
  std::vector<SolarSystem*> *systems;
  SolarSystem	*star;
  QFont		font10, font8; 
  
  glColor3f(1.0f, 1.0f, 1.0f);
  font10.setPointSize(10);
  systems = this->gameCore->getSolarSystems();
  star = systems->at(system);
  str = star->getName();
  
  renderText(xToPick +  GAL_RENDER_TEXT_RIGHT_SPACE
	     , yToPick + GAL_RENDER_TEXT_DOWN_SPACE
	     , tr(str.c_str()), font10);
  font8.setPointSize(8);
  str.clear();
  switch(star->getColor())
    {
    case SOL_SYS_COLOR_RED:
      str += "Red ";
      break;
    case SOL_SYS_COLOR_YELLOW:
      str += "Yellow ";
      break;
    case SOL_SYS_COLOR_WHITE:
      str += "White ";
      break;
    case  SOL_SYS_COLOR_BLUE:
      str += "Blue ";
      break;
    }
  switch(star->getSize())
    {
    case SOL_SYS_BIG:
      str += "giant";
      break;
    case SOL_SYS_MEDIUM:
      str += "medium";
      break;
    case SOL_SYS_SMALL:
      str += "dwarf";
      break;
    }  
  
  renderText(xToPick +  GAL_RENDER_TEXT_RIGHT_SPACE
	     , yToPick + GAL_RENDER_TEXT_DOWN_SPACE + 13
	     , tr(str.c_str()), font8);  
}

void		GalaxyRender::startPicking(int x, int y)
{
  GLint		viewport[4];
  
  glSelectBuffer(GAL_RENDER_BUFSIZE, selectBuf);
  glRenderMode(GL_SELECT);

  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();

  glGetIntegerv(GL_VIEWPORT,viewport);
  gluPickMatrix(x,  (this->windowH - y),
		10, 10, viewport);
  
  gluPerspective(45.0f, this->ratio , 1.0, 1000.0);
  glMatrixMode(GL_MODELVIEW);
}

int		GalaxyRender::stopPicking(int level)
{
  GLint		hits;
  GLuint	names, *ptr;
  unsigned int	j;
  int		i, countMatch = 0;
    

  // std::cout << "stopPicking" << std::endl;
  // restoring the original projection matrix
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
  glFlush();
  
  // returning to normal rendering mode
  hits = glRenderMode(GL_RENDER);
  this->pickingMode = GAL_RENDER_PICKING_MODE_OFF;

  ptr = (GLuint *) selectBuf;
  for (i = 0; i < hits; i++) 
    {
      names = *ptr;
      ptr++;
      ptr++;
      if((float) *ptr/0x7fffffff > 1.0f)
	countMatch++;
      ptr++;
      for (j = 0; j < names; j++) 
	{
	  if(countMatch == level)
	    {
	      return(*ptr);
	    }
	  ptr++;
	}
    }
  return(0);
}
